package de.stefanocke.roo.wicket.templates

import de.stefanocke.roo.wicket.Stereotypes
import de.stefanocke.roo.wicket.RooFactoryMethod

class FormFieldsTemplate extends JavaHtmlTemplate{
	
	override String javaClassNameWhenNew(){
		"FormFieldsPanel"
	}
	
	override String stereotype(){
		Stereotypes::FORM_FIELDS
	}
	
	override subPackageToUseWhenNew(){
		"common"
	}
		
	override buildJavaContents(){
		
		import_("java.util.Arrays")
		import_("java.util.Calendar")
		import_("java.util.Date")
		import_("java.util.List")
		
		import_("org.apache.wicket.Component")
		import_("org.apache.wicket.MarkupContainer")
		import_("org.apache.wicket.extensions.markup.html.form.DateTextField")
		import_("org.apache.wicket.extensions.yui.calendar.DatePicker")
		import_("org.apache.wicket.markup.html.WebMarkupContainer")
		import_("org.apache.wicket.markup.html.form.CheckBox")
		import_("org.apache.wicket.markup.html.form.DropDownChoice")
		import_("org.apache.wicket.markup.html.form.EnumChoiceRenderer")
		import_("org.apache.wicket.markup.html.form.Form")
		import_("org.apache.wicket.markup.html.form.FormComponent")
		import_("org.apache.wicket.markup.html.form.IChoiceRenderer")
		import_("org.apache.wicket.markup.html.form.IFormModelUpdateListener")
		import_("org.apache.wicket.markup.html.form.NumberTextField")
		import_("org.apache.wicket.markup.html.form.TextField")
		import_("org.apache.wicket.markup.html.panel.Panel")
		import_("org.apache.wicket.model.CompoundPropertyModel")
		import_("org.apache.wicket.model.IModel")
		import_("org.apache.wicket.model.LoadableDetachableModel")
			
		val DecoratorFragment = import_(typeof(DecoratorFragmentTemplate).generatedJavaType)
		val ListEditorPanel = import_(typeof(ListEditorTemplate).generatedJavaType) 	
		import_(typeof(RooFactoryMethod));
		
		
		'''	
		«stereotypeAnnotation»				
		public abstract class «simpleTypeName»<T> extends Panel implements IFormModelUpdateListener{
		
			/***/
			private static final String CPM_CONTAINER_ID = "cpm";
		
			/***/
			private static final long serialVersionUID = 1L;
		
			/**
			 * The default ID for a FormFields component
			 */
			public static final String FORMFIELDS = "formfields";
		
			public «simpleTypeName»() {
				super(FORMFIELDS);
				init();
			}
		
			public «simpleTypeName»(IModel<T> model) {
				super(FORMFIELDS, model);
				init();
			}
		
			public «simpleTypeName»(String id) {
				super(id);
				init();
			}
		
			public «simpleTypeName»(String id, IModel<T> model) {
				super(id, model);
				init();
			}
		
			protected void init() {
				WebMarkupContainer cpmContainer = new WebMarkupContainer(CPM_CONTAINER_ID);
				List<Component> fields = createFields();
				cpmContainer.add(fields.toArray(new Component[0]));
				add(cpmContainer);
		
			}
		
			@Override
			protected void onInitialize() {
				super.onInitialize();
				Form<?> form = findParent(Form.class);
				if (form != null) {
					onInitForm(form);
				}
				// This is the first time, the CompoundPropertyModel of the parent is
				// available.
				// So, onModelChanged is called, to set the proper model for the form
				// fields.
				onModelChanged();
			}
		
			@SuppressWarnings("unchecked")
			@Override
			protected void onModelChanged() {
		
				IModel<T> model = (IModel<T>) getDefaultModel();
		
				T modelObject = (T) getDefaultModelObject();
				if (modelObject == null) {
					// Create a new model which creates a new model object on load
					model = createNewModel();
				}
				get(CPM_CONTAINER_ID).setDefaultModel(new CompoundPropertyModel<T>(model));
				super.onModelChanged();
			}
		
			public FormComponent<?> getFormComponent(String path) {
				Component component = get(CPM_CONTAINER_ID).get(path);
				if (component != null && component instanceof SimpleFormField) {
					return ((SimpleFormField) component).getComponent();
				} else {
					throw new IllegalArgumentException("No form component found for path " + path);
				}
			}
		
			/**
			 * Allows for contributions to the parent form. F.e. adding form validators.
			 */
			protected void onInitForm(Form<?> form) {
			}
		
			/**
			 * Creates a new model for the embedded object. By default, it will
			 * reflectively create a new instance on load.
			 * 
			 * @param clazz
			 *            the type of the model
			 * 
			 * @return the new model
			 */
			protected IModel<T> createNewModel() {
		
				return new LoadableDetachableModel<T>() {
					/***/
					private static final long serialVersionUID = 1L;
		
					@Override
					protected T load() {
						return createNewModelObject();
					}
				};
		
			}
		
			@SuppressWarnings("unchecked")
			@Override
			public void updateModel() {
				setDefaultModelObject((T) get(CPM_CONTAINER_ID).getDefaultModelObject());
			}
		
			protected T createNewModelObject() {
		
				if (getObjectClass() == null) {
					throw new IllegalArgumentException("ObjectClass is null. Please override getObjectClass().");
				}
				try {
					T newInstance = getObjectClass().newInstance();
					return newInstance;
				} catch (Exception e) {
					throw new RuntimeException(e);
				}
			}
		
			protected Class<T> getObjectClass() {
				return null;
			}
		
			/**
			 * Gets model
			 * 
			 * @return model
			 */
			@SuppressWarnings("unchecked")
			public final IModel<T> getModel() {
				return (IModel<T>) getDefaultModel();
			}
		
			/**
			 * Sets model
			 * 
			 * @param model
			 */
			public final void setModel(IModel<T> model) {
				setDefaultModel(model);
			}
		
			protected abstract List<Component> createFields();
		
			/**
			 * A Fragment holding a form component and its decorations.
			 * 
			 * @author stoc
			 * 
			 */
			public static abstract class SimpleFormField extends «DecoratorFragment»<FormComponent<?>> {
		
				/***/
				private static final long serialVersionUID = 1L;
		
				/**
				 * The fragment providing the decorations for the form component.
				 */
				private static final String FRAGMENT_ID = "simpleFormField";
				
				/**
				 * 
				 * @param id
				 *            the id. Will be used to bind the form component to the
				 *            CPM.
				 * @param fcMarkupId
				 *            the markup id of the form component.
				 * @param markupProvider
				 *            the class, whose markup contains the fragment markup for
				 *            this fragment and the form component fragment.
				 * @param formComponent
				 *            the form component
				 */
				public SimpleFormField(String id, String fragmentId, MarkupContainer markupProvider) {
					super(id, FRAGMENT_ID, fragmentId, markupProvider);
				}
		
				@Override
				protected void initDecoration(FormComponent<?> c) {	
				}	
		
			}
		
			/**
			 * A Fragment holding a complex form component and its decorations. A
			 * complex form component
			 * is one that consists of multiple other form components. It is usually
			 * some kind of panel.
			 * 
			 * @author stoc
			 * 
			 */
			@RooStereotype("wicket.ComplexFormField")
			public abstract static class ComplexFormField extends «DecoratorFragment»<Component> {
		
				/***/
				private static final long serialVersionUID = 1L;
		
				/**
				 * The fragment providing the decorations for the form component.
				 */
				private static final String FRAGMENT_ID = "complexFormField";
		
				/**
				 * 
				 * @param id
				 *            the id. Will be used to bind the form component to the
				 *            CPM.
				 * @param fcMarkupId
				 *            the markup id of the form component.
				 * @param markupProvider
				 *            the class, whose markup contains the fragment markup for
				 *            this fragment and the form component fragment.
				 * @param formComponent
				 *            the form component
				 */
				public ComplexFormField(String id, MarkupContainer markupProvider) {
					super(id, FRAGMENT_ID, markupProvider);
				}
				
				@Override
				protected void initDecoration(Component component) {
				}
				
				@Override
				@RooStereotype("wicket.createComponent")
				protected abstract Component createComponent(String id);
		
			}
			
			@RooStereotype("wicket.EnumSelect")
			public abstract static class EnumField<E extends Enum<E>> extends SimpleFormField {
		
				/***/
				private static final long serialVersionUID = 1L;
		
				/**ITD Workaround*/
				public EnumField(){
					super(null, null, null);
				}
				
				public EnumField(String id, MarkupContainer markupProvider) {
					super(id, "select", markupProvider);
				}
				
				public EnumField(String id, IModel<E> model, MarkupContainer markupProvider) {
					super(id, "select", markupProvider);
					setDefaultModel(model); //TODO
				}
				
				@Override
				protected DropDownChoice<E> createComponent(String id) {
					return new DropDownChoice<E>(id, getChoices(), new EnumChoiceRenderer<E>(this));
				}
				
				protected List<? extends E> getChoices(){
					return Arrays.asList(getEnumValues());
					
				}
				
				protected abstract E[] getEnumValues();
		
			}
			
			@RooStereotype(value="wicket.EntitySelect")
			public static abstract class SimpleEntitySelect<E> extends SimpleFormField {
		
				/***/
				private static final long serialVersionUID = 1L;
				
				/**
				 * This constructor is only a worakround for ITD based code generation.
				 * Don't use it!
				 */
				public SimpleEntitySelect() {
					super(null, null, null);
				}
		
				public SimpleEntitySelect(String id, MarkupContainer markupProvider) {
					super(id, "select", markupProvider);
				}
		
				public SimpleEntitySelect(String id, IModel<E> model, MarkupContainer markupProvider) {
					super(id, "select", markupProvider);
					setDefaultModel(model);
				}
		
				protected IModel<? extends List<? extends E>> createChoicesModel() {
		
					return new LoadableDetachableModel<List<? extends E>>() {
		
						private static final long serialVersionUID = 1L;
		
						@Override
						protected List<? extends E> load() {
							return loadChoices();
						}
		
					};
				}
				
				@Override
				protected DropDownChoice<E> createComponent(String id) {
					DropDownChoice<E> dropDownChoice = new DropDownChoice<E>(id, createChoicesModel(), createRenderer());
					return dropDownChoice;
				}
		
				protected abstract List<? extends E> loadChoices();
		
				protected IChoiceRenderer<? super E> createRenderer() {
					return new IChoiceRenderer<E>() {
		
						private static final long serialVersionUID = 1L;
		
						@Override
						public Object getDisplayValue(E entity) {
							return SimpleEntitySelect.this.getDisplayValue(entity);
						}
		
						@Override
						public String getIdValue(E entity, int index) {
							return SimpleEntitySelect.this.getIdValue(entity).toString();
						}
					};
				}
		
				protected Object getDisplayValue(E entity) {
					return getDefaultModelObjectAsString(entity);
				}
		
				protected abstract Object getIdValue(E entity);
		
			}
		
			@RooStereotype("wicket.ListEditorField")
			public static abstract class ListEditorField<T> extends ComplexFormField {
		
				private static final long serialVersionUID = 1L;
				
				public ListEditorField(String id, MarkupContainer markupProvider) {
					super(id, markupProvider);
				}
				
				@Override
				protected «ListEditorPanel»<T> createComponent(String id) {
					return new «ListEditorPanel»<T>(id) {
		
						/***/
						private static final long serialVersionUID = 1L;
						
						@Override
						protected Component createItemComponent(String id) {
							return ListEditorField.this.createItemComponent(id);
						}
					};
				}
		
				protected abstract Component createItemComponent(String id);
				
			}
			
			
			
			@RooFactoryMethod(modelClasses = Boolean.class)
			protected SimpleFormField createCheckBox(String id) {
				return new SimpleFormField(id, "checkBox", this) {
					/***/
					private static final long serialVersionUID = 1L;
		
					@Override
					protected FormComponent<?> createComponent(String id) {
						return new CheckBox(id);
					}
				};
			}
		
			@RooFactoryMethod(modelClasses = { Date.class, Calendar.class })
			protected SimpleFormField createDateTimeField(String id, final Class<?> type) {
				return new SimpleFormField(id, "dateTimeField", this) {
		
					/***/
					private static final long serialVersionUID = 1L;
		
					@Override
					protected FormComponent<?> createComponent(String id) {
						DateTextField dtf = new DateTextField(id);
						dtf.setType(type);
						dtf.add(new DatePicker());
						return dtf;
					}
		
				};
			}
		
			@RooFactoryMethod(modelClasses = Number.class)
			protected <N extends Number & Comparable<N>> SimpleFormField createNumberField(String id, final Class<N> type) {
		
				return new SimpleFormField(id, "numberField", this) {
		
					/***/
					private static final long serialVersionUID = 1L;
		
					@Override
					protected FormComponent<?> createComponent(String id) {
						NumberTextField<N> ntf = new NumberTextField<N>(id);
						ntf.setType(type);
						return ntf;
					}
		
				};
			}
		
			@RooFactoryMethod(modelClasses = String.class)
			protected SimpleFormField createTextField(String id) {
				return new SimpleFormField(id, "textField", this) {
		
					/***/
					private static final long serialVersionUID = 1L;
		
					@Override
					protected FormComponent<?> createComponent(String id) {
						return new TextField<String>(id);
					}
		
				};
			}
		
			/**
			 * Fallback, if there are no more specific factory methods or components for
			 * the considered bean property.
			 * Creates a TextField an assumes the user to register an appropriate
			 * converter.
			 * 
			 * @param id
			 *            the id
			 * @param type
			 *            the type
			 * @return the TextFieldPanel
			 */
			@RooFactoryMethod
			protected <S> SimpleFormField createTextField(String id, final Class<S> type) {
		
				return new SimpleFormField(id, "textField", this) {
					/***/
					private static final long serialVersionUID = 1L;
		
					@Override
					protected FormComponent<?> createComponent(String id) {
						TextField<S> textField = new TextField<S>(id);
						textField.setType(type);
						return textField;
					}
		
				};
			}
		}
		'''
	}
	
	override buildHtmlBody(){
		'''
		<wicket:panel>
			<wicket:container wicket:id="cpm">		
				<wicket:child />
			</wicket:container>
		</wicket:panel>
		
		<!-- A simple form field with its decorations. A simple form field is one that consists 
		 of a single html input element. -->
		<wicket:fragment wicket:id="simpleFormField">
			<div class="field">
				<label wicket:id="label" class="fieldlabel">field label</label>
				<wicket:container wicket:id="container"/>		
			</div>
		</wicket:fragment>
		
		<!-- A complex form field with its decorations. A complex form field is one that consists 
		 of a multiple html input elements. -->
		<wicket:fragment wicket:id="complexFormField">
			<div>
				<fieldset>
					<legend wicket:id="label"></legend>
					<div class="fieldset">
						<wicket:container wicket:id="comp"/>
					</div>
				</fieldset>
			</div>
		</wicket:fragment>
		
		<wicket:fragment wicket:id="textField">		
			<input wicket:id="comp"></input>
		</wicket:fragment>
		
		<wicket:fragment wicket:id="numberField">	
			<input type="number" wicket:id="comp"></input>
		</wicket:fragment>
		
		<wicket:fragment wicket:id="dateTimeField">	
			<input wicket:id="comp"></input>
		</wicket:fragment>
		
		<wicket:fragment wicket:id="checkBox">	
			<input type="checkbox" wicket:id="comp"></input>
		</wicket:fragment>
		
		<wicket:fragment wicket:id="select">		
			<select wicket:id="comp"></select>
		</wicket:fragment>
		'''
	}
}